import { e, style } from "../utils/html";
import { app } from "../utils/global";
import { MapEditor } from "../components/MapEditor";
import { button } from "../components/Button";
import { saveToClipboard, save, toBase64, toPythonExpression, openLocalFile } from "../utils/file";
import overlay from "../components/Overlay";
import { PageClass } from "./PageClass";
import { getText } from "../utils/translate";

// style
const pageContent = {
  width: "100%",
  maxWidth: "720px",
  margin: "0 auto",
}
const mapEditorCanvas = {
  border: "1px solid",
  overflow: "auto",
}

export class PageMapEditor extends PageClass{
  constructor() {
    super();
    this.__setupElement(16, 8);
    this.__listenCurrentTileMapSelectorChange = this.__listenCurrentTileMapSelectorChange.bind(this);
    this.__render = this.__render.bind(this);
    this.needRender = false;
    this.visible = false;
    app.register(app.k.EVENT_CURRENT_TILE_MAP_SELECTOR_CHANGE, this.__listenCurrentTileMapSelectorChange);
  }

  __setupElement(w=16, h=8, map=undefined){
    // must init after PageTileSelector
    let tileMapSelector = app.get(app.k.CURRENT_TILE_MAP_SELECTOR);
    this.mapEditor = new MapEditor(tileMapSelector, w, h,
      Math.floor(Math.min(256/w,256/h)),
      this.__geidClick,
      this.__gridHover
    );
    style(this.mapEditor.element, {width:"100%"});
    if (map instanceof Uint8Array && map.length === this.mapEditor.mapTileList.length){
      this.mapEditor.mapTileList.set(map);
    }else{
    }
    // loading until canvas ready
    this.__render();
    // init or create new element
    if (this.canvasArea instanceof HTMLElement){
      this.canvasArea.removeChild(this.canvasArea.children[0]);
      this.canvasArea.appendChild(this.mapEditor.element);
      this.element.style.maxWidth = "720px";
      this.element.style.width = "none";
      return;
    }
    this.canvasArea = e("div", {style: mapEditorCanvas}, this.mapEditor.element)
    // element
    this.element = e("div",{style: pageContent},
      e("p", {style:{width:"100%",textAlign:"center"}}, getText("map_editor_top_tips")),
      // button bar
      e("div", {style:{display:"flex",flexDirection:"column",alignItems:"center"}},
        e("div", {style:{display:"flex",justifyContent:"space-around",alignItems:"center",width:"100%"}},
          button(getText("map_editor_op_button_new_map"), this.__btnNewMap.bind(this)),
          button(getText("map_editor_op_button_open_map"), this.__btnOpenMap.bind(this)),
          button(getText("map_editor_op_button_save_map"), this.__btnSaveMap.bind(this)),
          button(getText("map_editor_op_button_scale"), this.__btnToggleWidthLimit.bind(this)),
        ),
      ),
      // canvas
      this.canvasArea,
    );
  }

  __listenCurrentTileMapSelectorChange(_, index){
    // only render when visible
    if (index >= 0){
      if (this.visible){
        this.mapEditor.render(index);
      }else{
        if (this.mapEditor.mapTileList.includes(index)){
          this.needRender = true;
        }
      }
    }else{
      let tileSelector = app.get(app.k.CURRENT_TILE_MAP_SELECTOR);
      if (this.visible){
        this.mapEditor.setTileSelector(tileSelector, true);
      }else{
        this.mapEditor.setTileSelector(tileSelector, false);
        this.needRender = true;
      }
    }
  }

  async __render(){
    let close = overlay.loading();
    await this.mapEditor.render();
    this.needRender = false;
    close();
  }

  async __render2(){
    let close = overlay.loading();
    await this.mapEditor.renderPromise;
    close();
  }
  
  // MapEditor callback
  __geidClick(x, y, mapEditor, event){
    // click event
    event.preventDefault();
    let index = app.get(app.k.SELECT_TILE_INDEX, -1);
    if (index < 0){
      // alert("You should select a tile before edit tile map!")
      return; // no select tile
    }
    if (event.buttons === 1){
      mapEditor.setTileIndexAt(x, y, index);
    }else if (event.buttons === 2){
      mapEditor.setTileIndexAt(x, y, 0);
    }
  }

  __gridHover(x, y, mapEditor, event){
    // hover event
    event.preventDefault();
    let index = app.get(app.k.SELECT_TILE_INDEX, -1);
    if (index < 0){
      return; // no select tile
    }
    if (event.buttons === 1){
      mapEditor.setTileIndexAt(x, y, index, false);
    }else if (event.buttons === 2){
      mapEditor.setTileIndexAt(x, y, 0, false);
    }
    mapEditor.hoverTileIndexAt(x, y, index);
  }

  // button callback
  __btnToggleWidthLimit(){
    let scale = Math.floor(Math.min(256/this.mapEditor.width,256/this.mapEditor.height));
    if (this.element.style.maxWidth !== "none"){
      this.element.style.maxWidth = "none";
      this.element.style.width = this.mapEditor.element.width * Math.max(Math.floor(4/scale), 1);
    }else{
      this.element.style.maxWidth = "720px";
      this.element.style.width = "none";
    }
  }

  async __btnNewMap(){
    let ask = await overlay.confirm(getText("map_editor_text_prompt_new_map"));
    if (!ask) return;
    let width = await overlay.prompt(getText("map_editor_text_prompt_map_width"));
    let height = await overlay.prompt(getText("map_editor_text_prompt_map_height"));
    try{
      width = Number.parseInt(width);
      height = Number.parseInt(height);
      if (!Number.isInteger(width)||!Number.isInteger(height)||width<1 || width>256 || height<1 || height>256){
        throw new Error(getText("map_editor_text_error_map_size_error"));
      }
      this.__setupElement(width, height);
    }catch(err){
      overlay.alert(getText("map_editor_text_error_map_create_failed"));
      console.error(err);
    }
  }

  async __btnOpenMap(){
    let ask = await overlay.confirm(getText("map_editor_text_prompt_open_map"));
    if (!ask) return;
    let file = await openLocalFile();
    if (! file instanceof File){
      return;
    }
    let info = await overlay.confirm(getText("map_editor_text_prompt_map_has_info"));
    try{
      let buffer = await file.arrayBuffer();
      let data = new Uint8Array(buffer);
      let width, height;
      // without basic info
      if (info){
        width = data[0] + 1;
        height = data[1] + 1;
        data = data.slice(2);
      }else{
        let widthText = await overlay.prompt(getText("map_editor_text_prompt_map_width"));
        let heightText = await overlay.prompt(getText("map_editor_text_prompt_map_height"));
        width = Number.parseInt(widthText);
        height = Number.parseInt(heightText);
        if (!Number.isInteger(width)||!Number.isInteger(height)||width<1 || width>256 || height<1 || height>256){
          throw new Error(getText("map_editor_text_error_map_size_error"));
        }
      }
      this.__setupElement(width, height, data);
    }catch(err){
      overlay.alert(getText("map_editor_text_error_map_open_failed"));
      console.error(err);
    }
  }

  async __btnSaveMap(){
    let name = await overlay.prompt(getText("map_editor_text_prompt_choose_filename"));
    if (!name) return;
    let info = await overlay.confirm(getText("map_editor_text_prompt_map_with_info"));
    let data = this.mapEditor.mapTileList;
    if (info) {
      let tdata = new Uint8Array(data.length+2);
      tdata.set(data, 2);
      tdata[0] = this.mapEditor.width-1;
      tdata[1] = this.mapEditor.height-1;
      data = tdata;
    }
    let method
    while (!["file","base64","python"].includes(method)){
      method = await overlay.prompt(getText("map_editor_text_prompt_map_save_format"));
    }
    let text = "";
    switch(method){
      case "file": save(data, `${name}.${info?"map":"raw"}`); return;
      case "base64": saveToClipboard(text = toBase64(data)); break;
      case "python": saveToClipboard(text = toPythonExpression(data)); break;
      default: break;
    }
    overlay.alert(`${getText("map_editor_text_alert_copy")}:\n${text}`);
  }
  
  // life time managerment
  onMount(){
    if (this.needRender){
      this.__render();
    }
    this.visible = true;
  }

  onUnmount(){
    this.visible = false;
  }
}